package com.ruiyi.adrobot.mongo.taskdetail.impl;

import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import com.ruiyi.adrobot.beans.Fun;
import com.ruiyi.adrobot.beans.TaskDetail;
import com.ruiyi.adrobot.beans.TaskErrorCode;
import com.ruiyi.adrobot.beans.TaskReport;
import com.ruiyi.adrobot.beans.common.MongoPageRequest;
import com.ruiyi.adrobot.mongo.fun.FunRepository;
import com.ruiyi.adrobot.mongo.taskdetail.TaskDetailOperations;
import com.ruiyi.adrobot.utils.DateUtil;
import com.ruiyi.adrobot.utils.LoggerUtil;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.util.StringUtils;

import java.util.*;

import static org.springframework.data.mongodb.core.aggregation.Aggregation.newAggregation;
import static org.springframework.data.mongodb.core.query.Criteria.where;


/**
 * Created by Lenovo on 2017/5/9.
 */
public class TaskDetailRepositoryImpl implements TaskDetailOperations {
    @Autowired
    private MongoOperations mongo;
    @Autowired
    private FunRepository funRepository;

    @Override
    public TaskDetail getLastTaskByFun(Integer fun) {
        Query query = new Query(where("fun").is(fun));
        List<Sort.Order> orders = new ArrayList<Sort.Order>();
        orders.add(new Sort.Order(Sort.Direction.DESC, "createDate"));
        Sort sort = new Sort(orders);
        query.with(sort);
        List<TaskDetail> list = mongo.find(query, TaskDetail.class);
        return mongo.findOne(query, TaskDetail.class);
    }

    @Override
    public void findAndMudifFinishedById(String id, boolean finished) {
        Query query = new Query(where("_id").is((id)));
        Update update = new Update();
        update.set("finished", finished);
        mongo.findAndModify(query, update, TaskDetail.class);
    }

    @Override
    public void removeTemTask(String taskDetailId, String simCode) {
        Query query = new Query(new Criteria().andOperator(where("_id").is((taskDetailId)), where("temTasks").elemMatch(where("simCode").is(simCode))));
        Update update = new Update();
        update.pull("temTasks", new BasicDBObject("simCode", simCode));
        mongo.updateFirst(query, update, TaskDetail.class);
    }

    @Override
    public TaskDetail updateTaskByNum(String taskDetailId, Integer num, Update update) {
        Query query = new Query(new Criteria().andOperator(where("id").is(taskDetailId), where("tasks").elemMatch(where("num").is(num))));
        return mongo.findAndModify(query, update, TaskDetail.class);
    }


    @Override
    public TaskDetail updateById(String taskDetailId, Update update) {
        Query query = new Query(where("id").is(taskDetailId));
        return mongo.findAndModify(query, update, TaskDetail.class);
    }

    @Override
    public TaskDetail findByTaskDetailIdAndTaskNum(String taskDetailId, Integer num) {
        Query query = new Query(where("id").is(taskDetailId));
        query.fields().elemMatch("tasks", where("num").is(num));
        query.fields().include("vpnHost");
        return mongo.findOne(query, TaskDetail.class);
    }


    @Override
    public Page<TaskDetail> getListByPage(MongoPageRequest<Map<String, Object>> mongoPageRequest) {
        Map<String, Object> param = mongoPageRequest.getParams();
        Query query = new Query();
        if (null != param.get("startTimeLevel") && param.get("startTimeLevel").toString().equals("1")) {
            query.addCriteria(where("startTime").gt(DateUtil.getCurrentTimeStart()).lt(DateUtil.getCurrentTimeEnd()));
        }
        query.fields().exclude("tasks");
        Long count = mongo.count(query, TaskDetail.class);
        List<Sort.Order> orders = new ArrayList<Sort.Order>();
        orders.add(new Sort.Order(Sort.Direction.DESC, "createDate"));
        Sort sort = new Sort(orders);
        mongoPageRequest.setSort(sort);
        List<TaskDetail> taskDetails = mongo.find(query.with(mongoPageRequest), TaskDetail.class);
        List<Fun> funs = funRepository.findAll();
        Map<Integer, String> funsMap = new HashMap<>();
        for (Fun fun : funs) {
            funsMap.put(fun.getFun(), fun.getDescribe());
        }
        Iterator<TaskDetail> it = taskDetails.iterator();
        while (it.hasNext()) {
            TaskDetail taskDetail = it.next();
            taskDetail.setFunName(funsMap.get(taskDetail.getFun()));
        }
        return new PageImpl<TaskDetail>(taskDetails, mongoPageRequest, count);
    }

    @Override
    public TaskDetail.Task findTaskByIdAndTasksPhone(String id, String phone) {
        Query query = new Query(where("id").is(id));
        query.fields().elemMatch("tasks", where("phone").is(phone));
        return mongo.findOne(query, TaskDetail.class).getTasks().get(0);
    }

    @Override
    public TaskDetail.Task findTaskByIdAndTasksAccountInfoUserName(String id, String userName) {
        Query query = new Query(where("id").is(id));
        query.fields().elemMatch("tasks", where("accountInfo.userName").is(userName));
        return mongo.findOne(query, TaskDetail.class).getTasks().get(0);
    }

    @Override
    public List<String> findUsedImeisById(String taskDetailId) {
        Query query = new Query(where("id").is(taskDetailId));
        query.fields().include("usedImeis");
        TaskDetail taskDetail = mongo.findOne(query, TaskDetail.class);
        return taskDetail.getUsedImeis() == null ? Collections.EMPTY_LIST : taskDetail.getUsedImeis();
    }

    @Override
    /**
     * db.taskDetail.aggregate([
     { $match:{ "_id":ObjectId("5962d2ec45ce9450d24db8a0")}},
     {$unwind:"$tasks"},
     {$group:{_id:{errorCode:'$tasks.errorCode',flowId:'$tasks.flowId'},count: { $sum: 1 }}},
     { $sort : { "_id.flowId" : -1 } }
     ]
     );
     */
    public List<TaskErrorCode> getErrorCodeByGroup(String id, Integer fun) {
        List<Sort.Order> orders = new ArrayList<Sort.Order>();
        orders.add(new Sort.Order(Sort.Direction.DESC, "_id.flowId"));
        Sort sort = new Sort(orders);
        Aggregation agg = newAggregation(
                Aggregation.match(Criteria.where("_id").is(new ObjectId(id))),
                Aggregation.project("tasks"),
                Aggregation.unwind("tasks"),
                Aggregation.match(Criteria.where("tasks.errorCode").exists(true)),
                Aggregation.group("tasks.errorCode", "tasks.flowId", "tasks.stepNum").count().as("count"),
                Aggregation.sort(sort)
        );
        AggregationResults<TaskErrorCode> outputType = mongo.aggregate(agg, "taskDetail", TaskErrorCode.class);
        Iterator<TaskErrorCode> it = outputType.getMappedResults().iterator();
        Map<String, String> flowIdMap = new HashMap<>();
        for (Fun.Flow flow : funRepository.findByFun(fun).getFlows()) {
            flowIdMap.put(flow.getId(), flow.getDescribe());
        }

        while (it.hasNext()) {
            TaskErrorCode taskErrorCode = it.next();
            taskErrorCode.setFlowName(flowIdMap.get(taskErrorCode.getFlowId()));
        }
        return outputType.getMappedResults();
    }

    /**
     * db.taskDetail.aggregate([
     * { $match:{"createDate" : { "$gte" : "2017-07-10 00:00:00" , "$lt" : "2017-07-11 23:59:59" } }},
     * {$unwind:"$tasks"},
     * { $match:{"tasks.reported":false,"tasks.success":true,"tasks.errorCode":0}},
     * {$group:{_id:{fun:"$fun",errorCode:'$tasks.errorCode',flowId:'$tasks.flowId'},count: { $sum: 1 }}},
     * ]
     * );
     */
    @Override
    public List<TaskReport> findByReportedAndCreateDateBetween(String startDate, String endDate) {
        Aggregation agg = newAggregation(
                Aggregation.match(Criteria.where("createDate").gt(startDate).lt(endDate)),
                Aggregation.unwind("tasks"),
                Aggregation.match(Criteria.where("tasks.reported").is(false).and("tasks.success").is(true)),
                Aggregation.group("fun", "tasks.errorCode", "tasks.flowId").count().as("successCount")
        );
        AggregationResults<TaskReport> outputType = mongo.aggregate(agg, "taskDetail", TaskReport.class);
        List<TaskReport> taskReports = outputType.getMappedResults();

        Query query = new Query(new Criteria().andOperator(where("createDate").gt(startDate).lt(endDate), where("tasks").elemMatch(where("reported").is(false).and("success").is(true))));
        mongo.findAndModify(query, new Update().set("tasks.$.reported", true), TaskDetail.class);

        // 查询出所有流程对应流程名
        Map<String, String> flowMap = funRepository.findFlowMap();
        Map<Integer, String> funMap = funRepository.findFunMap();
        Iterator<TaskReport> it = taskReports.iterator();
        while (it.hasNext()) {
            TaskReport taskReport = it.next();
            taskReport.setFlowName(flowMap.get(taskReport.getFlowId()));
            taskReport.setFunName(funMap.get(taskReport.getFun()));
        }
        return taskReports;
    }

    @Override
    public List<TaskDetail.Task> findTasksByTaskDetailIdAndNotSent(String id) {
        Aggregation agg = newAggregation(
                Aggregation.match(Criteria.where("_id").is(new ObjectId(id))),
                Aggregation.unwind("tasks"),
                Aggregation.match(Criteria.where("tasks.sent").is(false)),
                Aggregation.project("tasks.flowId", "tasks.num", "tasks.startTime", "tasks.success", "tasks.sent", "tasks.stepType3")
        );
        AggregationResults<TaskDetail.Task> outputType = mongo.aggregate(agg, "taskDetail", TaskDetail.Task.class);
        return outputType.getMappedResults();
    }

}
